#include <stdlib.h>
#include <math.h>
#include <gtk/gtk.h>
#include "tetris.h"

#define TAILLE_CARRE 16
   
GtkWidget *create_arrow_button( GtkArrowType  arrow_type );
gboolean Gauche( GtkWidget *widget, gpointer data );
gboolean Droite( GtkWidget *widget, gpointer data );
gboolean Bas( GtkWidget *widget, gpointer data );
gboolean expose_evt_reaction( GtkWidget *widget, GdkEventExpose *event, gpointer data );
void dessineCarre( cairo_t* cr, int ligne, int colonne, char c );
void dessineGrille( cairo_t* cr, Grille g );
void initialiseJeu(Jeu *jeu);
void dessinePiece( cairo_t* cr, Jeu* jeu );
void nouvellePiece(Jeu *jeu);

/**
 * <b>Fonction : main</b>
 * 
 * Fonction principale
 */
int main( int   argc, char *argv[] )
{    
	Jeu jeu;
	/* améliore la fonction aléatoire "rand" */
	srand(time(NULL)); 
	initialiseJeu(&jeu);
	
	GtkWidget *window;

    /* Passe les arguments à GTK, pour qu'il extrait ceux qui le concernent.*/
     
    gtk_init (&argc, &argv);
    
    /*Crée une fenêtre. */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    /* La rend visible. */
    gtk_widget_show  (window);
    
    /*
    *	Création des conteneurs
    */
    GtkWidget* hbox_principal = gtk_hbox_new (TRUE, 10);
    GtkWidget* vbox_gauche = gtk_vbox_new (FALSE, 10);
	GtkWidget* vbox_droite = gtk_vbox_new (TRUE, 10);
	GtkWidget* hbox_gauche_bas = gtk_hbox_new (TRUE, 10);
	GtkWidget* hbox_droite_haut = gtk_hbox_new (TRUE, 10);
	GtkWidget* hbox_droite_milieu = gtk_hbox_new (TRUE, 10);
    GtkWidget* hbox_droite_bas = gtk_hbox_new (TRUE, 10);
    
    /*
    *	Création des boutons
    */
    GtkWidget* bouton_new;
    GtkWidget* bouton_quit;
    GtkWidget* bouton_fleche_gauche;
    GtkWidget* bouton_fleche_bas;
    GtkWidget* bouton_fleche_droite;
    
    
   /*
    *	Création des labels.
    */
    
    GtkWidget* label_score = gtk_label_new( "Score" );
    GtkWidget* label_delay = gtk_label_new( "Delay" );
    
    /*
    *	Création et initialisation de la zone de dessin.
    */
    GtkWidget* drawing_area = gtk_drawing_area_new ();
	/* largeur= TAILLE_CARRE*(LARGEUR+4) pixels, hauteur = TAILLE_CARRE*(LARGEUR+7) pixels. */
	gtk_widget_set_size_request (drawing_area, TAILLE_CARRE*(LARGEUR+4), TAILLE_CARRE*(HAUTEUR+7));
	
	/* ... votre zone de dessin s'appelle ici "drawing_area" */
	g_signal_connect( G_OBJECT (drawing_area), "expose_event", G_CALLBACK (expose_evt_reaction), &jeu );
					
    
    
	/*
	 * 	Initialisation des boutons.
	 */
	bouton_new = gtk_button_new_with_label ( "New" );
	bouton_quit = gtk_button_new_with_label ( "Quit" );
	bouton_fleche_gauche = create_arrow_button(GTK_ARROW_LEFT);
	bouton_fleche_droite = create_arrow_button(GTK_ARROW_RIGHT);
	bouton_fleche_bas = create_arrow_button(GTK_ARROW_DOWN);
	
	g_signal_connect( bouton_fleche_gauche, "clicked", G_CALLBACK( Gauche ), &jeu );
	g_signal_connect( bouton_fleche_droite, "clicked", G_CALLBACK( Droite ), &jeu );
	g_signal_connect( bouton_fleche_bas, "clicked", G_CALLBACK( Bas ), &jeu );
	
	
	
	/* 
	 * Connecte la réaction gtk_main_quit à l'événement "clic" sur ce bouton. 
	 */
	g_signal_connect( bouton_quit, "clicked", G_CALLBACK( gtk_main_quit ), NULL);
	
	
	/*
    *	Imbrications des composants
    */
    gtk_container_add( GTK_CONTAINER( window ), hbox_principal );
	gtk_container_add( GTK_CONTAINER( hbox_principal ), vbox_gauche );
	gtk_container_add( GTK_CONTAINER( hbox_principal ), vbox_droite );
	gtk_container_add( GTK_CONTAINER( vbox_gauche ), drawing_area );
	gtk_container_add( GTK_CONTAINER( vbox_gauche ), hbox_gauche_bas );
	gtk_container_add( GTK_CONTAINER( vbox_droite ), hbox_droite_haut );
	gtk_container_add( GTK_CONTAINER( hbox_droite_milieu ), label_score );
	gtk_container_add( GTK_CONTAINER( hbox_droite_bas ), label_delay );
	gtk_container_add( GTK_CONTAINER( vbox_droite ), hbox_droite_milieu );
	gtk_container_add( GTK_CONTAINER( vbox_droite ), hbox_droite_bas );
	
	/*
	 *  Ajout des boutons aux composants
	 */
	gtk_container_add ( GTK_CONTAINER ( hbox_droite_haut ), bouton_new );
	gtk_container_add ( GTK_CONTAINER ( hbox_droite_haut ), bouton_quit );
	gtk_container_add ( GTK_CONTAINER ( hbox_gauche_bas ), bouton_fleche_gauche );
	gtk_container_add ( GTK_CONTAINER ( hbox_gauche_bas ), bouton_fleche_bas );
	gtk_container_add ( GTK_CONTAINER ( hbox_gauche_bas ), bouton_fleche_droite );
	
	
	/* 
	 * Rend visible les composants
	 */
	gtk_widget_show( hbox_principal );
	gtk_widget_show( drawing_area );
	gtk_widget_show( vbox_gauche );
	gtk_widget_show( vbox_droite );
	gtk_widget_show( hbox_gauche_bas );
	gtk_widget_show( hbox_droite_haut );
	gtk_widget_show( hbox_droite_milieu );
	gtk_widget_show( hbox_droite_bas );
	gtk_widget_show( bouton_new );
	gtk_widget_show( bouton_quit );
	gtk_widget_show( bouton_fleche_gauche );
	gtk_widget_show( bouton_fleche_bas );
	gtk_widget_show( bouton_fleche_droite );
	gtk_widget_show( label_score );
	gtk_widget_show( label_delay );
	
	
	/*
	 * Affichage du grapique
	 */
	gtk_main ();
	
	return 0;
}
 
/**
 * 
 * 
 */
void initialiseJeu(Jeu *jeu)
{
	initialiseGrille(jeu->g);
	genererPieces(jeu->tab);
	jeu->col=(LARGEUR-jeu->piece.largeur)/2;
	jeu->score=0;
	jeu->delay=0;
}

/**
 * <b>Fonction : create_arrow_button</b>
 * 
 * Créer un boutton de type flèche
 * 
 * @param arrow_type Type de la flèche à créer
 * @return un bouton de type flèche
 */
GtkWidget *create_arrow_button( GtkArrowType  arrow_type )
{ /* Les boutons sont en fait des conteneurs à un seul élément. */
	GtkWidget* button = gtk_button_new ();
	GtkWidget* arrow = gtk_arrow_new (arrow_type, GTK_SHADOW_OUT);
	gtk_container_add (GTK_CONTAINER (button), arrow);
	gtk_widget_show( arrow );
	return button;
}

 /**
  * <b>Fonction : Gauche</b>
  * 
  * Fonction a appeler quand la fleche de gauche est cliquée
  * Va permettre de décaler la colonne à laquelle sera placée la pièce
  * 
  * @param data Des données correpondant à la structure Jeu
  */
gboolean Gauche( GtkWidget *widget, gpointer data )
 {
   /* Recupère la valeur passée en paramètre. */
   Jeu* jeu = (Jeu*) data; 
   jeu->col--;
   return TRUE; /* Tout s'est bien passé */
 }
 
 /**
  * <b>Fonction : Droite</b>
  * 
  * Fonction a appeler quand la fleche de droite est cliquée
  * Va permettre de décaler la colonne à laquelle sera placée la pièce
  * 
  * @param data Des données correpondant à la structure Jeu
  */
gboolean Droite( GtkWidget *widget, gpointer data )
 {
	/* Recupère la valeur passée en paramètre. */
	Jeu* jeu = (Jeu*) data;
	jeu->col++;
	return TRUE; /* Tout s'est bien passé */
 }
 
 /**
  * <b>Fonction : Bas</b>
  * 
  * Fonction a appeler quand la fleche du bas est cliquée.
  * Fait déscendre la pièce dans la colonne pointée.
  * 
  * @param data Des données correpondant à la structure Jeu
  */
gboolean Bas( GtkWidget *widget, gpointer data )
 {	
	 cairo_t* cr = gdk_cairo_create (widget->window);
	 Piece p;
	/* Recupère la valeur passée en paramètre. */
	
	Jeu* jeu = (Jeu*) data; 
	
	return TRUE;
 }
 
 /**
  * <b>Fonction : expose_evt_reaction</b>
  * 
  * Appelé à chaque évènement, cette fonction va controler le dessin
  * 
  * @param *widget La zone de dessin
  * @param *event L'évenement appelant
  * @param data Des données correpondant à la structure Jeu
  * @return un boolean
  */
gboolean expose_evt_reaction( GtkWidget *widget, GdkEventExpose *event, gpointer data )
{
	/* c'est la structure qui permet d'afficher dans une zone de dessin */
	/* via Cairo  */
	Jeu* jeu = (Jeu*) data;
	Piece p;
	cairo_t* cr = gdk_cairo_create (widget->window);
	cairo_set_source_rgb (cr, 0, 0, 0);
	cairo_paint( cr );
	
	p = pieceAleatoire(jeu->tab);
	
	/*afficherPiece(p);*/
	
	int i = hauteurMax(jeu->g, jeu->col, jeu->col+p.largeur);
	ecrirePiece(jeu->g,p,jeu->col,i);
	
	dessineGrille(cr, jeu->g);
	
	
	/* nouvelle piece prête à tomber */
	nouvellePiece(jeu);
	
	
	/*dessineGrille(cr, jeu->g);*/
	
	dessinePiece(cr, jeu);
	afficheGrille(jeu->g);
	
	cairo_destroy (cr);
	return TRUE;
}

/**
 * <b>Fonction : dessineCarre</b>
 * 
 * Dessine le caractère donnée à l'emplacement donnée. 
 * Affiche une couleur différente en fonction de la couleur :
 * <ul>
 * <li>'|' : Bleu</li>
 * <li>'@' : Rouge</li>
 * <li>'?' : Bleu</li>
 * <li>'&' : Jaune</li>
 * <li>'$' : Vert</li>
 * <li>'#' : Rose</li>
 * <li>' ' : Blanc</li>
 * </ul>
 * 
 * @param c le caratère permettant de connaitre la couleur du carré
 * @param colonne la colonne de la zone à laquelle on va dessiner le carré
 * @param ligne la ligne de la zone à laquelle on va dessiner le carré
 * @param cr une strucute caira_t* correspondant à la zone de dessin 
 */
void dessineCarre( cairo_t* cr, int ligne, int colonne, char c )
{
	int r,g,b;
	cairo_rectangle (cr, colonne*TAILLE_CARRE, ligne*TAILLE_CARRE, TAILLE_CARRE, TAILLE_CARRE );
	
	switch (c)
	{
		case '|' :
			r=0,g=1,b=0;
			cairo_set_source_rgb (cr, 0, 1, 0);
			break;
		
		case '@' :
			r=1,g=0,b=0;
			cairo_set_source_rgb (cr, 1, 0, 0);
			break;
		
		case '?' :
			r=0,g=0,b=1;
			cairo_set_source_rgb (cr, 0, 0, 1);
			break;
		
		case '&' :
			r=1,g=1,b=0;
			cairo_set_source_rgb (cr, 1, 1, 0);
			break;
		
		case '$' :
			r=0,g=1,b=1;
			cairo_set_source_rgb (cr, 0, 1, 1);
			break;
		
		case '#' :
			r=1,g=0,b=1;
			cairo_set_source_rgb (cr, 1, 0, 1);
			break;
		
		case ' ' :
			r=1,g=1,b=1;
			cairo_set_source_rgb (cr, 1, 1, 1);
			break;
		
		default :
			printf("couleur par défaut\n");
			cairo_set_source_rgb (cr, 1, 1, 1);
			break;
	}
	
	
	cairo_fill_preserve(cr);
	
	cairo_set_line_width(cr, 3);
	
	cairo_rectangle (cr, colonne*TAILLE_CARRE, ligne*TAILLE_CARRE, TAILLE_CARRE, TAILLE_CARRE );	

	cairo_fill_preserve(cr);
	
	cairo_stroke( cr );		
}

/**
 * <b>Fonction : dessineGrille</b>
 * 
 * Fonction qui permet de déssiner chaque carractère de la grille du jeu
 * 
 * @param cr une structure caira_t* correspondant à la zone de dessein
 * @param g la grille de jeu a dessiner
 */
void dessineGrille( cairo_t* cr, Grille g )
{
	int i, j;
	int decaleX=3;
	int decaleY=1	;
	
	for (i=decaleX;i<HAUTEUR+decaleX;i++)
	{
		for (j=decaleY;j<LARGEUR+decaleY;j++)
		{
			dessineCarre(cr,i+decaleX,j+decaleY,lireCase(g,i-decaleX,j-decaleY));
		}
	}
}

void nouvellePiece(Jeu *jeu)
{
	jeu->piece = pieceAleatoire(jeu->tab);
	afficherPiece(jeu->piece);
	jeu->col=(LARGEUR-jeu->piece.largeur)/2;
}

void dessinePiece( cairo_t* cr, Jeu* jeu )
{
	
	
	
	int i,j;
	for ( j=0; j<jeu->piece.largeur; j++ ) 
	
	{

		for (i=jeu->piece.hauteur-1; i>=0; --i) 
		    dessineCarre(cr,i,jeu->col+j+2,jeu->piece.forme[i][j]);
	}
}




